import cv2
import numpy as np
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
import threading
import time
from datetime import datetime
import os
from PIL import Image, ImageTk


class VideoProcessorApp:
    def __init__(self, root):
        """初始化视频处理应用"""
        self.root = root
        self.root.title("计算机视觉视频处理应用")
        self.root.geometry("1200x700")
        self.root.configure(bg="#f0f0f0")

        # 设置中文字体
        self.font = ('SimHei', 10)

        # 视频相关变量
        self.cap = None
        self.video_path = None
        self.current_frame = None
        self.playing = False
        self.paused = False
        self.total_frames = 0
        self.current_frame_pos = 0
        self.fps = 0
        self.video_width = 0
        self.video_height = 0

        # 计算机视觉处理相关变量
        self.processing_enabled = False
        self.motion_detection_enabled = False
        self.face_detection_enabled = False
        self.color_filter_enabled = False
        self.feature_extraction_enabled = False

        # 颜色过滤参数
        self.lower_color = np.array([0, 0, 0])
        self.upper_color = np.array([255, 255, 255])

        # 创建界面
        self.create_widgets()

        # 初始化人脸检测器
        self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

        # 初始化背景减除器用于运动检测
        self.fgbg = cv2.createBackgroundSubtractorMOG2()

        # 初始化特征检测器
        self.feature_detector = cv2.ORB_create()

        # 线程控制
        self.stop_event = threading.Event()
        self.processing_thread = None

    def create_widgets(self):
        """创建GUI组件"""
        # 创建主框架
        main_frame = tk.Frame(self.root, bg="#f0f0f0")
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 顶部控制栏
        control_frame = tk.Frame(main_frame, bg="#e0e0e0", height=50)
        control_frame.pack(fill=tk.X, padx=5, pady=5)

        # 文件选择按钮
        tk.Button(control_frame, text="打开视频文件", command=self.open_file, font=self.font,
                  bg="#4CAF50", fg="white", padx=10, pady=5).pack(side=tk.LEFT, padx=5)

        tk.Button(control_frame, text="打开摄像头", command=self.open_camera, font=self.font,
                  bg="#2196F3", fg="white", padx=10, pady=5).pack(side=tk.LEFT, padx=5)

        # 视频控制按钮
        self.play_btn = tk.Button(control_frame, text="播放", command=self.play_video, font=self.font,
                                  state=tk.DISABLED, padx=10, pady=5)
        self.play_btn.pack(side=tk.LEFT, padx=5)

        self.pause_btn = tk.Button(control_frame, text="暂停", command=self.pause_video, font=self.font,
                                   state=tk.DISABLED, padx=10, pady=5)
        self.pause_btn.pack(side=tk.LEFT, padx=5)

        self.stop_btn = tk.Button(control_frame, text="停止", command=self.stop_video, font=self.font,
                                  state=tk.DISABLED, padx=10, pady=5)
        self.stop_btn.pack(side=tk.LEFT, padx=5)

        # 进度控制
        self.speed_scale = ttk.Scale(control_frame, from_=0.5, to=2.0, orient=tk.HORIZONTAL,
                                     length=100, value=1.0)
        self.speed_scale.pack(side=tk.LEFT, padx=5)
        tk.Label(control_frame, text="播放速度:", font=self.font, bg="#e0e0e0").pack(side=tk.LEFT)

        # 进度条
        progress_frame = tk.Frame(control_frame, bg="#e0e0e0")
        progress_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10)

        self.time_label = tk.Label(progress_frame, text="00:00/00:00", font=self.font, bg="#e0e0e0")
        self.time_label.pack(side=tk.LEFT, padx=5)

        self.progress_bar = ttk.Scale(progress_frame, orient=tk.HORIZONTAL, value=0, command=self.seek)
        self.progress_bar.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)

        # 视频显示区域
        display_frame = tk.Frame(main_frame, bg="#222222")
        display_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        self.video_label = tk.Label(display_frame, bg="#000000")
        self.video_label.pack(fill=tk.BOTH, expand=True)

        # 控制面板
        control_panel = tk.Frame(main_frame, bg="#e0e0e0", height=100)
        control_panel.pack(fill=tk.X, padx=5, pady=5)

        # 计算机视觉功能选择
        cv_frame = tk.LabelFrame(control_panel, text="计算机视觉功能", font=self.font, bg="#e0e0e0")
        cv_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)

        self.motion_var = tk.BooleanVar()
        tk.Checkbutton(cv_frame, text="运动检测", variable=self.motion_var, command=self.toggle_motion_detection,
                       font=self.font, bg="#e0e0e0").pack(side=tk.LEFT, padx=10)

        self.face_var = tk.BooleanVar()
        tk.Checkbutton(cv_frame, text="人脸识别", variable=self.face_var, command=self.toggle_face_detection,
                       font=self.font, bg="#e0e0e0").pack(side=tk.LEFT, padx=10)

        self.color_var = tk.BooleanVar()
        tk.Checkbutton(cv_frame, text="颜色过滤", variable=self.color_var, command=self.toggle_color_filter,
                       font=self.font, bg="#e0e0e0").pack(side=tk.LEFT, padx=10)

        self.feature_var = tk.BooleanVar()
        tk.Checkbutton(cv_frame, text="特征提取", variable=self.feature_var, command=self.toggle_feature_extraction,
                       font=self.font, bg="#e0e0e0").pack(side=tk.LEFT, padx=10)

        # 颜色过滤控制
        color_frame = tk.LabelFrame(control_panel, text="颜色过滤设置", font=self.font, bg="#e0e0e0")
        color_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)

        color_options = ["无", "蓝色", "绿色", "红色"]
        self.color_combo = ttk.Combobox(color_frame, values=color_options, state="readonly", width=10)
        self.color_combo.current(0)
        self.color_combo.bind("<<ComboboxSelected>>", self.set_color_filter)
        self.color_combo.pack(side=tk.LEFT, padx=10)

        # 其他功能按钮
        other_frame = tk.LabelFrame(control_panel, text="其他功能", font=self.font, bg="#e0e0e0")
        other_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)

        tk.Button(other_frame, text="截图", command=self.take_screenshot, font=self.font,
                  bg="#FF5722", fg="white", padx=10, pady=2).pack(side=tk.LEFT, padx=5)

        self.record_var = tk.BooleanVar()
        self.record_btn = tk.Button(other_frame, text="开始录制", command=self.toggle_recording, font=self.font,
                                    bg="#F44336", fg="white", padx=10, pady=2)
        self.record_btn.pack(side=tk.LEFT, padx=5)

        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = tk.Label(self.root, textvariable=self.status_var, bd=1, relief=tk.SUNKEN, anchor=tk.W,
                              font=self.font)
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)

    def open_file(self):
        """打开视频文件"""
        self.stop_video()

        file_path = filedialog.askopenfilename(
            title="选择视频文件",
            filetypes=[("视频文件", "*.mp4;*.avi;*.mov;*.mkv;*.flv")]
        )

        if file_path:
            self.video_path = file_path
            self.open_video()

    def open_camera(self, camera_id=0):
        """打开摄像头"""
        self.stop_video()

        try:
            self.cap = cv2.VideoCapture(camera_id)

            if not self.cap.isOpened():
                messagebox.showerror("错误", f"无法打开摄像头 {camera_id}")
                return

            self.fps = self.cap.get(cv2.CAP_PROP_FPS)
            self.video_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            self.video_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

            self.total_frames = float('inf')  # 摄像头视为无限帧
            self.current_frame_pos = 0

            self.status_var.set(f"已连接摄像头 {camera_id}")
            self.update_ui_state(is_camera=True)
            self.play_video()

        except Exception as e:
            messagebox.showerror("错误", f"打开摄像头时出错: {str(e)}")

    def open_video(self):
        """打开视频文件"""
        try:
            self.cap = cv2.VideoCapture(self.video_path)

            if not self.cap.isOpened():
                messagebox.showerror("错误", "无法打开视频文件")
                return

            self.fps = self.cap.get(cv2.CAP_PROP_FPS)
            self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
            self.video_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            self.video_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

            self.current_frame_pos = 0
            self.progress_bar.config(to=self.total_frames)

            self.status_var.set(f"已加载视频: {os.path.basename(self.video_path)}")
            self.update_ui_state(is_camera=False)

            # 显示第一帧
            ret, frame = self.cap.read()
            if ret:
                self.current_frame = frame
                self.display_frame()

        except Exception as e:
            messagebox.showerror("错误", f"打开视频时出错: {str(e)}")

    def update_ui_state(self, is_camera=False):
        """更新UI状态"""
        self.play_btn.config(state=tk.NORMAL)
        self.pause_btn.config(state=tk.DISABLED)
        self.stop_btn.config(state=tk.NORMAL)

        # 摄像头模式下禁用进度条和速度控制
        if is_camera:
            self.progress_bar.config(state=tk.DISABLED)
            self.speed_scale.config(state=tk.DISABLED)
        else:
            self.progress_bar.config(state=tk.NORMAL)
            self.speed_scale.config(state=tk.NORMAL)

    def play_video(self):
        """播放视频"""
        if self.cap is None:
            return

        if not self.playing:
            self.playing = True
            self.paused = False
            self.play_btn.config(state=tk.DISABLED)
            self.pause_btn.config(state=tk.NORMAL)

            # 如果是新视频或已停止，需要重新设置帧位置
            if self.current_frame_pos == 0 or (
                    self.current_frame_pos >= self.total_frames and self.total_frames != float('inf')):
                self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
                self.current_frame_pos = 0

            # 启动处理线程
            self.stop_event.clear()
            self.processing_thread = threading.Thread(target=self.process_video_frames)
            self.processing_thread.daemon = True
            self.processing_thread.start()

    def pause_video(self):
        """暂停视频"""
        if self.playing:
            self.playing = False
            self.paused = True
            self.play_btn.config(state=tk.NORMAL)
            self.pause_btn.config(state=tk.DISABLED)

    def stop_video(self):
        """停止视频播放"""
        self.playing = False
        self.paused = False
        self.play_btn.config(state=tk.NORMAL)
        self.pause_btn.config(state=tk.DISABLED)

        # 停止处理线程
        if self.processing_thread and self.processing_thread.is_alive():
            self.stop_event.set()
            self.processing_thread.join(timeout=1.0)

        # 重置帧位置
        if self.cap and self.total_frames != float('inf'):  # 不是摄像头
            self.current_frame_pos = 0
            self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame_pos)
            self.update_progress()

    def seek(self, value):
        """调整视频进度"""
        if self.cap and self.total_frames != float('inf'):  # 不是摄像头
            frame_pos = int(float(value))
            if 0 <= frame_pos < self.total_frames:
                self.current_frame_pos = frame_pos
                self.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_pos)
                self.update_progress()

    def process_video_frames(self):
        """处理视频帧的线程函数"""
        # 录制相关设置
        recording = False
        out = None

        while not self.stop_event.is_set() and self.playing:
            start_time = time.time()

            # 读取帧
            ret, frame = self.cap.read()

            if not ret:
                # 视频结束
                self.playing = False
                self.root.after(0, self.update_ui_state)
                break

            self.current_frame = frame

            # 应用计算机视觉处理
            if self.processing_enabled:
                processed_frame = self.apply_computer_vision(frame.copy())
            else:
                processed_frame = frame

            # 更新显示
            self.display_frame(processed_frame)

            # 更新进度
            if self.total_frames != float('inf'):  # 不是摄像头
                self.current_frame_pos += 1
                self.root.after(0, self.update_progress)

                if self.current_frame_pos >= self.total_frames:
                    self.playing = False
                    self.root.after(0, self.update_ui_state)
                    break

            # 录制视频
            if recording and out:
                out.write(processed_frame)

            # 控制播放速度
            elapsed_time = time.time() - start_time
            delay = max(1, int((1.0 / self.fps) / float(self.speed_scale.get()) * 1000 - elapsed_time * 1000))
            time.sleep(delay / 1000.0)

        # 释放录制资源
        if out:
            out.release()

    def apply_computer_vision(self, frame):
        """应用计算机视觉处理"""
        # 添加时间戳
        cv2.putText(frame, datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
                    (10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 1)

        # 应用运动检测
        if self.motion_detection_enabled:
            frame, motion = self.detect_motion(frame)
            if motion:
                cv2.putText(frame, "运动检测", (10, 30),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

        # 应用人脸识别
        if self.face_detection_enabled:
            frame = self.detect_faces(frame)

        # 应用颜色过滤
        if self.color_filter_enabled:
            frame = self.apply_color_filter(frame)

        # 应用特征提取
        if self.feature_extraction_enabled:
            frame = self.extract_features(frame)

        return frame

    def detect_motion(self, frame):
        """检测视频中的运动物体"""
        motion_detected = False

        # 应用背景减除
        fgmask = self.fgbg.apply(frame)

        # 形态学操作去除噪声
        kernel = np.ones((5, 5), np.uint8)
        fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)

        # 寻找轮廓
        contours, _ = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        for contour in contours:
            if cv2.contourArea(contour) > 500:  # 过滤小面积
                # 绘制边界框
                (x, y, w, h) = cv2.boundingRect(contour)
                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
                motion_detected = True

        return frame, motion_detected

    def detect_faces(self, frame):
        """检测人脸"""
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 检测人脸
        faces = self.face_cascade.detectMultiScale(
            gray,
            scaleFactor=1.1,
            minNeighbors=5,
            minSize=(30, 30),
            flags=cv2.CASCADE_SCALE_IMAGE
        )

        # 在人脸周围绘制矩形
        for (x, y, w, h) in faces:
            cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
            cv2.putText(frame, "人脸", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)

        return frame

    def apply_color_filter(self, frame):
        """应用颜色过滤"""
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        mask = cv2.inRange(hsv, self.lower_color, self.upper_color)
        result = cv2.bitwise_and(frame, frame, mask=mask)
        return result

    def extract_features(self, frame):
        """提取图像特征"""
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        kp, des = self.feature_detector.detectAndCompute(gray, None)
        frame = cv2.drawKeypoints(frame, kp, None, color=(0, 255, 0), flags=0)
        return frame

    def toggle_motion_detection(self):
        """切换运动检测功能"""
        self.motion_detection_enabled = self.motion_var.get()
        self.update_processing_state()

    def toggle_face_detection(self):
        """切换人脸识别功能"""
        self.face_detection_enabled = self.face_var.get()
        self.update_processing_state()

    def toggle_color_filter(self):
        """切换颜色过滤功能"""
        self.color_filter_enabled = self.color_var.get()
        self.update_processing_state()

    def toggle_feature_extraction(self):
        """切换特征提取功能"""
        self.feature_extraction_enabled = self.feature_var.get()
        self.update_processing_state()

    def update_processing_state(self):
        """更新处理状态"""
        self.processing_enabled = (self.motion_detection_enabled or
                                   self.face_detection_enabled or
                                   self.color_filter_enabled or
                                   self.feature_extraction_enabled)

        # 更新状态消息
        if self.processing_enabled:
            active_features = []
            if self.motion_detection_enabled:
                active_features.append("运动检测")
            if self.face_detection_enabled:
                active_features.append("人脸识别")
            if self.color_filter_enabled:
                active_features.append("颜色过滤")
            if self.feature_extraction_enabled:
                active_features.append("特征提取")

            self.status_var.set(f"正在应用: {', '.join(active_features)}")
        else:
            self.status_var.set("未应用计算机视觉处理")

    def set_color_filter(self, event=None):
        """设置颜色过滤范围"""
        color = self.color_combo.get()

        if color == "无":
            self.lower_color = np.array([0, 0, 0])
            self.upper_color = np.array([255, 255, 255])
        elif color == "蓝色":
            self.lower_color = np.array([100, 150, 0])
            self.upper_color = np.array([140, 255, 255])
        elif color == "绿色":
            self.lower_color = np.array([40, 50, 50])
            self.upper_color = np.array([80, 255, 255])
        elif color == "红色":
            # 红色在HSV中分为两部分
            self.lower_color1 = np.array([0, 150, 0])
            self.upper_color1 = np.array([10, 255, 255])
            self.lower_color2 = np.array([170, 150, 0])
            self.upper_color2 = np.array([180, 255, 255])

    def take_screenshot(self):
        """截取当前帧"""
        if self.current_frame is not None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"screenshot_{timestamp}.jpg"

            # 保存原图
            cv2.imwrite(filename, self.current_frame)

            # 显示保存消息
            self.status_var.set(f"截图已保存为: {filename}")
            self.root.after(3000, lambda: self.status_var.set("就绪"))

    def toggle_recording(self):
        """开始/停止录制视频"""
        if not hasattr(self, 'recording') or not self.recording:
            # 开始录制
            self.recording = True
            self.record_btn.config(text="停止录制")

            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"recording_{timestamp}.avi"

            fourcc = cv2.VideoWriter_fourcc(*'XVID')
            self.out = cv2.VideoWriter(filename, fourcc, self.fps,
                                       (self.video_width, self.video_height))

            self.status_var.set(f"正在录制: {filename}")
        else:
            # 停止录制
            self.recording = False
            self.record_btn.config(text="开始录制")
            self.out.release()
            self.status_var.set(f"录制已保存为: {filename}")
            self.root.after(3000, lambda: self.status_var.set("就绪"))

    def display_frame(self, frame=None):
        """显示当前帧"""
        if frame is None:
            frame = self.current_frame

        if frame is not None:
            # 调整大小以适应窗口
            display_width = self.video_label.winfo_width()
            display_height = self.video_label.winfo_height()

            if display_width > 1 and display_height > 1:
                frame = cv2.resize(frame, (display_width, display_height))

            # 转换为RGB
            rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

            # 转换为Tkinter可以使用的格式
            img = Image.fromarray(rgb_frame)
            img_tk = ImageTk.PhotoImage(image=img)

            # 更新显示
            self.video_label.config(image=img_tk)
            self.video_label.image = img_tk

    def update_progress(self):
        """更新进度条和时间标签"""
        if self.total_frames != float('inf'):  # 不是摄像头
            self.progress_bar.set(self.current_frame_pos)

            # 计算当前时间和总时间
            current_seconds = int(self.current_frame_pos / self.fps)
            total_seconds = int(self.total_frames / self.fps)

            current_time = time.strftime('%M:%S', time.gmtime(current_seconds))
            total_time = time.strftime('%M:%S', time.gmtime(total_seconds))

            self.time_label.config(text=f"{current_time}/{total_time}")


if __name__ == "__main__":
    root = tk.Tk()
    app = VideoProcessorApp(root)


    # 窗口调整大小时重绘视频
    def on_resize(event):
        if event.widget == root and app.current_frame is not None:
            app.display_frame()


    root.bind("<Configure>", on_resize)

    root.mainloop()